#include "almacenamiento.h"


extern int32_t tamCache;

/**
 Funcion que carga toda la estructura del FileSystem incluyendo:
 -> Boot Sector. ( En sector cero ).
 -> Tamanio en bytes de Tabla FAT.
 -> Cantidad de Entradas de la FAT.
 -> Tabla FAT en uint32_t *
 **/

t_fs *cargar_fs(int32_t Sock) {

	t_fs *fs = malloc(sizeof(t_fs));

	char *bloque0 = pedir_bloque(0, Sock); //pido el boot sector al ppd

	memcpy(&fs->bootsector.bytesPorSector, bloque0 + 11, 2);
	memcpy(&fs->bootsector.sectorPerCluster, bloque0 + 13, 1);
	memcpy(&fs->bootsector.reservedSectorCount, bloque0 + 14, 2);
	memcpy(&fs->bootsector.numeroFATs, bloque0 + 16, 1);
	memcpy(&fs->bootsector.sectorPerFAT32, bloque0 + 36, 4);
	memcpy(&fs->bootsector.rootCluster, bloque0 + 44, 4); //comienzo del root dir
	memcpy(&fs->bootsector.rootFSInfo,bloque0+48,2);

	fs->tamFatBytes = fs->bootsector.sectorPerFAT32	* fs->bootsector.bytesPorSector; // Calculo el tama�o de la Tabla FAT
	fs->cantEntradasEnFAT = fs->tamFatBytes / TAM_ENTRADA_FAT; //Calculo cantidad total de Entradas de la Tabla FAT
	fs->tamCluster = fs->bootsector.sectorPerCluster* fs->bootsector.bytesPorSector;
	fs->entradasPorCluster = fs->tamCluster / TAM_ENTRADA_DIR; //Cantidad de entradas en un Cluster ( entradas de directorio )
	fs->tablaFAT = obtener_fat(fs, fs->bootsector.reservedSectorCount,fs->tamFatBytes, Sock);
	//creo una lista y despues le cargo las entradas del root
	fs->rootDirectoryContent = crear_entrada(); // ES UNA ESTRUCTURA CON LISTA DE ELEM y CANT_ELEM
	/** Cargo arbol de entradas **/
	fat32_cargar_entradas(fs, fs->bootsector.rootCluster, fs->rootDirectoryContent, Sock);
	return fs;
}

int32_t fat32_cargar_entradas(t_fs *fs, uint32_t NumCluster, Tentrada *entradas, int32_t Sock) {

	char *cluster = obtener_cluster_cacheG(fs, NumCluster, Sock); //pido el cluster al ppd
	char *entry;
	uint32_t tamNombreAux = 0, indice_entrada = 0;
	uint16_t tamUltimaEntLongName = 0;

	t_elemento *directorio, *arch;
	t_entrada* entrada;
	t_entrada_longfile *entradaLongname;
	uint16_t nombreUTF16[TAM_MAX_NOMBRE];
	memset(nombreUTF16, 0 ,TAM_MAX_NOMBRE);
	//char ultimaEntLongName[TAM_MAX_NOMBRE];
	char *ultimaEntLongName = (char*)calloc(1,TAM_MAX_NOMBRE);
	//memset(ultimaEntLongName, 0, TAM_MAX_NOMBRE);


	do {
		entry = cluster + indice_entrada * 32;
		while (ENT_DIR_ESTA_LIBRE( entry[0] )) {
			indice_entrada++;
			entry = cluster + indice_entrada * 32;
		}

		switch (entry[11]) { //atributos

		case ATR_LONGNAME:
			//break;
			entradaLongname = (t_entrada_longfile*)calloc(1,sizeof(t_entrada_longfile));
			//entradaLongname = (t_entrada_longfile*) (cluster + (indice_entrada * TAM_ENTRADA_DIR));
			memset(entradaLongname->nombrePart1, 0, 5);
			memset(entradaLongname->nombrePart2, 0, 6);
			memset(entradaLongname->nombrePart3, 0, 2);

			memcpy(&entradaLongname->sequenceNumber, cluster + (indice_entrada * TAM_ENTRADA_DIR), 1);
			memcpy(&entradaLongname->nombrePart1, cluster + (indice_entrada * TAM_ENTRADA_DIR) + 1, 10);
			memcpy(&entradaLongname->attributes, cluster + (indice_entrada * TAM_ENTRADA_DIR) + 11, 1);
			memcpy(&entradaLongname->reserved, cluster + (indice_entrada * TAM_ENTRADA_DIR) + 12, 1);
			memcpy(&entradaLongname->checksum, cluster + (indice_entrada * TAM_ENTRADA_DIR)+13, 1);
			memcpy(&entradaLongname->nombrePart2, cluster + (indice_entrada * TAM_ENTRADA_DIR) + 14, 12);
			memcpy(&entradaLongname->firstClusterNumber, cluster + (indice_entrada * TAM_ENTRADA_DIR) + 26, 2);
			memcpy(&entradaLongname->nombrePart3, cluster + (indice_entrada * TAM_ENTRADA_DIR) + 28, 4);

			tamUltimaEntLongName = 0;

			//uno todos los nombres que tiene la entrada longname
			memcpy(nombreUTF16, entradaLongname->nombrePart1,	sizeof(uint16_t) * 5);
			memcpy(nombreUTF16 + 5, entradaLongname->nombrePart2,	sizeof(uint16_t) * 6);
			memcpy(nombreUTF16 + 11, entradaLongname->nombrePart3, sizeof(uint16_t) * 2);

			while (nombreUTF16[tamNombreAux] != 0 && tamNombreAux < 13)
				tamNombreAux++;

			tamNombreAux = (tamNombreAux + 1) * 2;
			//paso el buffer de utf 16 a utf 8
			unicode_utf16_to_utf8_inbuffer(nombreUTF16, tamNombreAux, ultimaEntLongName, &tamUltimaEntLongName); //funcion de utils.c
			tamUltimaEntLongName = tamNombreAux/2-1;
			tamNombreAux = 0;

			break;

		case ATR_ARCHIVO:
			arch = malloc(sizeof(t_elemento));
			arch->tipo = ENTRADA_ARCH;
			entrada = (t_entrada*) entry;

			leerElemento(entrada, arch, ultimaEntLongName, tamUltimaEntLongName);
			arch->tamanio = entrada->fileSize;
			arch->clusterDirectorio = NumCluster;
			arch->numeroEntradaEnDir = indice_entrada;
			arch->cache = NULL;
			arch->cantAperturas = 0;

			if (arch != NULL)
				lista_agregar(entradas, arch);

			memset(ultimaEntLongName, '\0', TAM_MAX_NOMBRE);
			tamUltimaEntLongName = 0;
			break;

		case ATR_DIRECTORIO:
			directorio = malloc(sizeof(t_elemento));
			entrada = (t_entrada*) entry;

			leerElemento(entrada, directorio, ultimaEntLongName, tamUltimaEntLongName);

			directorio->tipo = ENTRADA_DIR;
			directorio->contenido = crear_entrada();
			directorio->clusterDirectorio = NumCluster;
			directorio->numeroEntradaEnDir = indice_entrada;
			directorio->cantAperturas = 0;

			if (directorio->primerCluster > 0 && strncmp(entrada->nombre, ".       ", 8) != 0 && strncmp(entrada->nombre, "..      ", 8) != 0) {

				fat32_cargar_entradas(fs, directorio->primerCluster, directorio->contenido, Sock);

				if (directorio != NULL)
					lista_agregar(entradas, directorio);


				memset(ultimaEntLongName, '\0', TAM_MAX_NOMBRE);
				tamUltimaEntLongName = 0;
			}
			break;

		default:
			if (!ENT_DIR_ES_ULTIMA( entry[0] )) {
				fprintf(stderr, "Entrada no Reconocida:0x%x\n", entry[11]);
			}
			break;

		}

		indice_entrada++;

		if (indice_entrada == fs->entradasPorCluster) {
			if (!ESFAT_EOC(fs->tablaFAT[NumCluster])) {
				cluster = obtener_cluster_cacheG(fs, fs->tablaFAT[NumCluster], Sock);
				indice_entrada = 0;
			} else
				break;
		}

	} while (!ENT_DIR_ES_ULTIMA( entry[0] ));

	return 0;
}

void leerElemento(t_entrada *entrada, t_elemento *elemento,	char *ultimaEntLongName, uint16_t tamUltimaEntLongName) {

	ARCH_OBT_FECHA(elemento->creationDate, entrada->creationDate);
	ARCH_OBT_HORA(elemento->creationTime, entrada->creationTime);
	int32_t largo = 0;

	largo = cargarNombre(elemento->nombre, entrada->nombre, ultimaEntLongName, tamUltimaEntLongName);

	elemento->largoNombre = largo;

	elemento->primerCluster = (entrada->firstClusterHi << 16) & 0xFFFF0000;
	elemento->primerCluster = elemento->primerCluster | entrada->firstClusterLow;
}

int32_t cargarNombre(char *nombreLargo, char* NombreCorto, char* EntLongName,uint16_t tamEntLongName) {
	int32_t i;

	if (EntLongName != '\0' && tamEntLongName > 0) {
		memcpy(nombreLargo, EntLongName, tamEntLongName);
		return tamEntLongName;
	}
	else {

		for (i = 0; NombreCorto[i] != ' ' && i < 8; i++);
		memset(nombreLargo, '\0',TAM_MAX_NOMBRE);
		memcpy(nombreLargo, NombreCorto, i); //cargo el nombre
		memcpy(nombreLargo + i + 1, NombreCorto + 8, 3);//cargo la extencion
		memcpy(nombreLargo + i,".",1);
		return (i+4);

	}
}
int32_t ObtenerEntradaLibre(t_fs *fs, char *cluster, uint32_t *NumCluster, int32_t Sock) {
	char *entradaLibre = NULL;
	int32_t indiceEntrada = 0;
	int32_t bloqueFat;

	//cluster = obtener_cluster_cacheG(fs, *NumCluster, Sock);

	uint32_t i;

	while (1) {
		entradaLibre = cluster + indiceEntrada * TAM_ENTRADA_DIR;

		if (ENT_DIR_ESTA_LIBRE( entradaLibre[0] ) || ENT_DIR_ES_ULTIMA( entradaLibre[0] )) {
			return indiceEntrada;
		}

		indiceEntrada++;
		//me fijo si se me terminaron las entradas del directorio
		if (indiceEntrada == fs->entradasPorCluster) {
			//chequeo si no hay siguiente cluster
			if (ESFAT_EOC(ObtenerSgteCluster(fs, *NumCluster))) {
				i = ObtenerUnClusLibre(fs, &bloqueFat);
				fs->tablaFAT[*NumCluster] = i;
				fs->tablaFAT[i] = FAT_EOC;
				escribir_bloque(16 + bloqueFat,fs->tablaFAT + (bloqueFat*1024), Sock);
				NumCluster = i;
				cluster = obtener_cluster_cacheG(fs, *NumCluster, Sock);
			} else {
				ObtenerSgteCluster(fs, *NumCluster);
				cluster = obtener_cluster_cacheG(fs, *NumCluster, Sock);
			}
			indiceEntrada = 0;
		}
	}

	return 0;
}

void crear_entrada_longname(char *nombre, char *cluster, int32_t indiceEntrada) {

	t_entrada_longfile entrada_longname;
	uint16_t nombreAuxBuff[13];
	uint32_t nombreAuxBuffSize = 13;
	//unsigned char checksum;
	//char nombreCorto[8];
	//memset(&entrada_longname, '\0', sizeof(t_entrada_longfile));

	unicode_utf8_to_utf16_inbuffer(nombre, strlen(nombre), nombreAuxBuff,&nombreAuxBuffSize);

	entrada_longname.attributes = ATR_LONGNAME;

	entrada_longname.sequenceNumber = ULTIMA_LONGNAME;

	memcpy(entrada_longname.nombrePart1, nombreAuxBuff, sizeof(uint16_t) * 5);
	memcpy(entrada_longname.nombrePart2, nombreAuxBuff + 5,sizeof(uint16_t) * 6);
	memcpy(entrada_longname.nombrePart3, nombreAuxBuff + 11,sizeof(uint16_t) * 2);

	//memcpy(nombreCorto, cluster + (indiceEntrada * (TAM_ENTRADA_DIR + 1)), 8);

	//checksum =  lfn_checksum(nombreCorto);
	//memcpy(&entrada_longname.checksum,checksum,sizeof(entrada_longname.checksum));
	memcpy(cluster + indiceEntrada * TAM_ENTRADA_DIR, &entrada_longname.sequenceNumber, sizeof(entrada_longname.sequenceNumber));
	memcpy(cluster + indiceEntrada * TAM_ENTRADA_DIR + 1, &entrada_longname.nombrePart1, sizeof(entrada_longname.nombrePart1));
	memcpy(cluster + indiceEntrada * TAM_ENTRADA_DIR + 11, &entrada_longname.attributes, sizeof(entrada_longname.attributes));
	memcpy(cluster + indiceEntrada * TAM_ENTRADA_DIR + 12, &entrada_longname.reserved, sizeof(entrada_longname.reserved));
	//memcpy(cluster + indiceEntrada * TAM_ENTRADA_DIR + 13, &entrada_longname.checksum, sizeof(entrada_longname.checksum));
	memcpy(cluster + indiceEntrada * TAM_ENTRADA_DIR + 14, &entrada_longname.nombrePart2, sizeof(entrada_longname.nombrePart2));
	//memcpy(cluster + indiceEntrada * TAM_ENTRADA_DIR + 26, &entrada_longname.firstClusterNumber, sizeof(entrada_longname.firstClusterNumber));
	memcpy(cluster + indiceEntrada * TAM_ENTRADA_DIR + 28, &entrada_longname.nombrePart3, sizeof(entrada_longname.nombrePart3));


	//memcpy(cluster + indiceEntrada * TAM_ENTRADA_DIR, &entrada_longname,32);

}

int32_t obt_direccion_cluster(t_fs *fs, int32_t numCluster) {

	int32_t tamanioFats = fs->bootsector.sectorPerFAT32
			* fs->bootsector.numeroFATs;
	return fs->bootsector.reservedSectorCount + tamanioFats
			+ (numCluster - 2) * fs->bootsector.sectorPerCluster;

	/*
	 num sector = Sectores reservados + Numero de FATs * Tama�o de la FAT en sectores +
	 ( Numero de Cluster - 2 ) * Sectores por cluster
	 */
}


void AgregarCluster(t_fs *fs, t_elemento *element, uint16_t cant, char* clus, int32_t Sock) {
	int32_t primerCluster = element->primerCluster,Clus;
	uint16_t i = 0;
	int32_t Clusnuevo, ClusAnt, ClusActual, bloqueFat;

	char* ClusVacio=malloc(TAM_CLUSTER);
	memset(ClusVacio,'\0',TAM_CLUSTER);

	if (primerCluster == 0) {
		Clusnuevo = ObtenerUnClusLibre(fs, &bloqueFat);
		escribir_cluster(fs,Clusnuevo ,ClusVacio,  Sock);

		AsignaPrimerCluster(fs, element, Clusnuevo,clus, Sock);
		fs->tablaFAT[Clusnuevo] = FAT_EOC;
		escribir_bloque(16 + bloqueFat,fs->tablaFAT + (bloqueFat*1024), Sock);
		i++;
		while (i < cant) {
			ClusAnt = Clusnuevo;
			Clusnuevo = ObtenerUnClusLibre(fs, &bloqueFat);
			escribir_cluster(fs,Clusnuevo ,ClusVacio,  Sock);

			fs->tablaFAT[ClusAnt] = Clusnuevo;
			escribir_bloque(16 + bloqueFat,fs->tablaFAT + (bloqueFat*1024), Sock);
			fs->tablaFAT[Clusnuevo] = FAT_EOC;
			escribir_bloque(16 + bloqueFat,fs->tablaFAT + (bloqueFat*1024), Sock);
			i++;
		}
	} else {
		if (ESFAT_EOC(fs->tablaFAT[primerCluster])) {
			Clusnuevo = primerCluster;
			while (i < cant) {
				ClusAnt = Clusnuevo;
				Clusnuevo = ObtenerUnClusLibre(fs, &bloqueFat);
				escribir_cluster(fs,Clusnuevo ,ClusVacio,  Sock);
				fs->tablaFAT[ClusAnt] = Clusnuevo;
				escribir_bloque(16 + bloqueFat,fs->tablaFAT + (bloqueFat*1024), Sock);
				fs->tablaFAT[Clusnuevo] = FAT_EOC;
				escribir_bloque(16 + bloqueFat,fs->tablaFAT + (bloqueFat*1024), Sock);
				i++;
			}
		} else {
			Clus = primerCluster;
			while (!ESFAT_EOC(fs->tablaFAT[Clus] )) {
				Clus = fs->tablaFAT[Clus];
			}
			ClusActual = Clus;
			while (i < cant) {
				ClusAnt = ClusActual;
				ClusActual = ObtenerUnClusLibre(fs, &bloqueFat);
				escribir_cluster(fs,ClusActual ,ClusVacio,  Sock);
				fs->tablaFAT[ClusAnt] = ClusActual;
				escribir_bloque(16 + bloqueFat,fs->tablaFAT + (bloqueFat*1024), Sock);
				fs->tablaFAT[ClusActual] = FAT_EOC;
				escribir_bloque(16 + bloqueFat,fs->tablaFAT + (bloqueFat*1024), Sock);
				i++;
			}
		}
	}
	free(ClusVacio);
}

void AsignaPrimerCluster(t_fs *fs, t_elemento *elemento, int32_t Cluster,char* Clus,int32_t Sock) {
	t_entrada *entrada = malloc(sizeof(t_entrada));
	entrada = (t_entrada *) (Clus + elemento->numeroEntradaEnDir * TAM_ENTRADA_DIR);
	entrada->firstClusterHi = (Cluster >> 16) & 0x0000FFFF;
	entrada->firstClusterLow = Cluster & 0x0000FFFF;
	escribir_cluster(fs, elemento->clusterDirectorio, Clus, Sock);
	elemento->primerCluster = Cluster;

}

void QuitarClusters(t_fs *fs, t_elemento *elemento, uint16_t cant,char* clus, int32_t Sock) {

	int32_t primerCluster = elemento->primerCluster;
	int32_t Clus, Prox, Aux;
	uint16_t cont = 1, i;

	if (primerCluster != 0) {
		if (ESFAT_EOC(fs->tablaFAT[primerCluster])) {
			fs->tablaFAT[primerCluster] = 0;
			escribir_bloque(16 + primerCluster*4/1024 ,fs->tablaFAT + ((primerCluster*4/1024)*1024), Sock);
			AsignaPrimerCluster(fs, elemento, 0, clus, Sock);
			return;
		} else
			//Consideramos que se encuentra dentro del rango de clusters usados ( si rompe es por aca)
			Clus = primerCluster;
		while (!ESFAT_EOC(fs->tablaFAT[Clus])) {
			cont++;
			Clus = fs->tablaFAT[Clus];
		}
		Clus = primerCluster;
		for (i = 0; i < (cont - cant); i++) {
			Aux = Clus;
			Clus = fs->tablaFAT[Clus];
		}
		if(cont-cant==0){
			Prox = fs->tablaFAT[Clus];
			fs->tablaFAT[Clus] = 0;
			escribir_bloque(16 + Clus*4/1024 ,fs->tablaFAT + ((Clus*4/1024)*1024), Sock);
			AsignaPrimerCluster(fs, elemento, 0, clus, Sock);
			while (!ESFAT_EOC(fs->tablaFAT[Prox] )) {
				Prox = fs->tablaFAT[Prox];
				fs->tablaFAT[Prox] = 0;
				escribir_bloque(16 + Prox*4/1024 ,fs->tablaFAT + ((Prox*4/1024)*1024), Sock);
			}
			fs->tablaFAT[Prox] = 0;
			escribir_bloque(16 + Prox*4/1024 ,fs->tablaFAT + ((Prox*4/1024)*1024), Sock);
		}
		else{
			//Prox = fs->tablaFAT[Clus];
			fs->tablaFAT[Aux] = FAT_EOC;
			while (!ESFAT_EOC(Clus )) {
				Prox = fs->tablaFAT[Clus];
				fs->tablaFAT[Clus] = 0;
				escribir_bloque(16 + Clus*4/1024 ,fs->tablaFAT + ((Clus*4/1024)*1024), Sock);
				Clus = Prox;

			}

		}
	}
}

void CambiarTamanioArch(t_fs *fs, t_elemento *arch, int32_t nuevoTam,char* Clus, int32_t Sock) {

	t_entrada *entrada = (t_entrada*)calloc(1,sizeof(t_entrada));
	memcpy(entrada,Clus + arch->numeroEntradaEnDir * TAM_ENTRADA_DIR, TAM_ENTRADA_DIR);
	entrada = (t_entrada*) (Clus + arch->numeroEntradaEnDir * TAM_ENTRADA_DIR);
	arch->tamanio = nuevoTam;
	entrada->fileSize = nuevoTam;
	memcpy(Clus + arch->numeroEntradaEnDir * TAM_ENTRADA_DIR, entrada, TAM_ENTRADA_DIR );
	escribir_cluster(fs, arch->clusterDirectorio, Clus, Sock);

}

uint32_t ObtenerSgteCluster(t_fs *fs, uint32_t ClusActual) {

	if (ESFAT_EOC(fs->tablaFAT[ClusActual])) {
		return 0;
	}

	return fs->tablaFAT[ClusActual];
}

int32_t cantClusters(t_fs *fs, t_elemento *elemento) {
	int32_t cluster = elemento->primerCluster;
	int32_t i = 1;
	if(cluster == 0)
		return 0;

	while (!ESFAT_EOC(fs->tablaFAT[cluster])) {//la fat tiene cualquier cosa en la pos..y desp tira sf
		cluster = fs->tablaFAT[cluster];
		i++;
	}

	return i;
}


t_elemento *obtener_elem(t_fs *fs, char *path,Tentrada** dirElemento) {

	char **pathCortado = string_split2(path + 1, '/'); //funcion de utils, que me devuelve un array con el path
	t_elemento *elemento;
	int indice = 0;


	elemento = obt_elem_aux(fs->rootDirectoryContent, pathCortado, dirElemento);//dirElemento vuelve con NULL!!!

	while (pathCortado[indice] != NULL) {
		free(pathCortado[indice]);
		indice++;
	}

	free(pathCortado);

	return elemento;

}

t_elemento *obt_elem_aux(Tentrada *directorioActual, char **pathCortado, Tentrada** dirElemento) {

	t_elemento *elemento;


	if (pathCortado[0] == NULL) {
		return NULL;
	}

	elemento = lista_buscar(directorioActual, pathCortado[0]);

	if (elemento == NULL) {
		return NULL;
	}

	if (elemento->tipo == ENTRADA_ARCH) {
		if (pathCortado[1] == NULL) { //si el path[0] es arch y el siguiente null, lo econtre

			*dirElemento = directorioActual;
			return elemento;
		} else {
			return NULL;
		}
	}
	if (elemento->tipo == ENTRADA_DIR) {
		if (pathCortado[1] != NULL) {
			return obt_elem_aux(((t_elemento*) elemento)->contenido,&pathCortado[1],dirElemento); //revisar si va dirElemento
		} else {
			*dirElemento = directorioActual;
			return elemento;
		}
	}

	return NULL;

}

bool resize_arch(t_fs *fs, t_elemento *arch, uint32_t nuevoTam, int32_t Sock) {
	int nuevoTamanio = ceil((double) nuevoTam / (double) fs->tamCluster);
	int tamanioActual = ceil((double) arch->tamanio / (double) fs->tamCluster);
	char *clus = NULL;

	clus = obtener_cluster_cacheG(fs,arch->clusterDirectorio,Sock);

	if (nuevoTamanio == tamanioActual){
		CambiarTamanioArch(fs, arch, nuevoTam, clus, Sock);
		return true;
	}


	if (nuevoTamanio > tamanioActual) {
		AgregarCluster(fs, arch, nuevoTamanio - tamanioActual, clus, Sock);

	} else {

		QuitarClusters(fs, arch, tamanioActual - nuevoTamanio, clus, Sock);

	}

	CambiarTamanioArch(fs, arch, nuevoTam, clus, Sock);

	return true;
}

size_t leer_arch(t_fs *fs, t_elemento *arch, char* buff, off_t offset,size_t tamACopiarTotal, int32_t Sock) {

	int32_t i = 0, flag =1;
	//long offsetClusters = floorl((double) offset / (double) fs->tamCluster);
	uint32_t offsetClusters = offset / fs->tamCluster;
	uint32_t numCluster = arch->primerCluster,numCluster1;
	char *cluster, *cluster1;
	int32_t tamCopiado = 0;
	int32_t tamACopiarAux;
	int32_t *vecClusters;
	t_vec_clusters *clusters;


	if (offset >= arch->tamanio) {
		return 0;
	}

	if (offset + tamACopiarTotal > arch->tamanio)
		tamACopiarTotal = arch->tamanio - offset;

	for (i = 0; i < offsetClusters; i++) {
		numCluster = ObtenerSgteCluster(fs, numCluster);
	}

	cluster1 = leer_cluster_cache_archivo(fs, arch, numCluster, Sock);
	//cluster = obtener_cluster_cacheG(fs, numCluster, Sock);

	cluster = cluster1 + (offset - offsetClusters * fs->tamCluster);

	tamACopiarAux = fs->tamCluster	- (offset - offsetClusters * fs->tamCluster);

	if (tamACopiarAux > tamACopiarTotal) {
		tamACopiarAux = tamACopiarTotal;
	}

	//if (tamACopiarTotal < 40000){
		do {

			memcpy(buff + tamCopiado, cluster, tamACopiarAux);
			if (flag == 1){
				free(cluster1);
				flag =0;
			}else
				free(cluster);
			tamCopiado = tamCopiado + tamACopiarAux;

			if (tamCopiado < tamACopiarTotal) {
				numCluster = ObtenerSgteCluster(fs, numCluster);
				//cluster = obtener_cluster_cacheG(fs, numCluster, Sock);
				cluster = leer_cluster_cache_archivo(fs, arch, numCluster, Sock);
				if (tamCopiado + fs->tamCluster <= tamACopiarTotal) {
					tamACopiarAux = fs->tamCluster;
				} else {
					tamACopiarAux = tamACopiarTotal - tamCopiado;
				}
			}

		} while (tamCopiado < tamACopiarTotal);
	/*}
	
	else{ //hago peticiones mas optimizadas para casos grandes
		
		memcpy(buff + tamCopiado, cluster, tamACopiarAux);	
		tamCopiado = tamCopiado + tamACopiarAux;

		do {
			if (tamCopiado < tamACopiarTotal) {
				numCluster = ObtenerSgteCluster(fs, numCluster);
				numCluster1 = ObtenerSgteCluster(fs,numCluster);
				
				//preguntar si el cluster es 0, quiere decir que no esta..
				
				clusters = obtener_clusters(numCluster,numCluster1);
				
				//en este espacio llenaria la cache de archivo
				
				//cluster = leer_cluster_cache_archivo(fs, arch, numCluster, Sock);
				
				if (tamCopiado + TAM_CLUSTER*2 <= tamACopiarTotal) {
					tamACopiarAux = fs->tamCluster * 2;
				} else {
					tamACopiarAux = tamACopiarTotal - tamCopiado;
				}
			}

			memcpy(buff + tamCopiado, clusters, tamACopiarAux);
			free(clusters);
			tamCopiado = tamCopiado + tamACopiarAux;

		} while (tamCopiado < tamACopiarTotal);
	}

	*/

	return tamCopiado;
}

void crear_arch(t_fs *fs, char *path, int32_t Sock) {

	t_elemento *dirPadre;
	char **pathCortado = string_split2(path + 1, '/');
	t_entrada *nuevaEntradaArch; //= (t_entrada*)calloc(1,TAM_ENTRADA_DIR);
	t_elemento *nuevoArch;
	char *nombre;
	char *clusterPadre;
	int32_t tamNombre, indiceEntradaArch, i = 0;
	int32_t dirClusterPadre = fs->bootsector.rootCluster;
	t_entrada **dirElemento = NULL;

	// Voy al final de pathCortado asi obtengo el nombre del archivo que esta al final
	while (pathCortado[i + 1] != NULL) {
		i++;
	}
	nombre = pathCortado[i];
	tamNombre = strlen(nombre);
	pathCortado[i] = NULL;

	dirPadre = obt_elem_aux(fs->rootDirectoryContent, pathCortado, dirElemento);

	if (dirPadre != NULL) {
		dirClusterPadre = dirPadre->primerCluster;
	}
	clusterPadre = obtener_cluster_cacheG(fs,dirClusterPadre,Sock);
	indiceEntradaArch = ObtenerEntradaLibre(fs, clusterPadre, &dirClusterPadre, Sock); //pido una entrada para la longname
	crear_entrada_longname(nombre, clusterPadre, indiceEntradaArch); //carga mal la longname!!!!!!!!!
	escribir_cluster(fs, dirClusterPadre, clusterPadre, Sock);

	indiceEntradaArch = ObtenerEntradaLibre(fs, clusterPadre, &dirClusterPadre, Sock); //pido otra para el archivo
	nuevaEntradaArch = (t_entrada*) (clusterPadre + indiceEntradaArch * TAM_ENTRADA_DIR);

	//cargo la extension del archivo sin el ".", y la saco del nombre
	if (tamNombre > 4 && nombre[tamNombre - 4] == '.') {
		memcpy(nuevaEntradaArch->nombre + 8, nombre + (tamNombre - 3), 3);
		tamNombre = tamNombre - 4;
	}
	//cargo la entrada
	if (tamNombre <= 8) {
		memcpy(nuevaEntradaArch->nombre, nombre, tamNombre);
	} else {
		memcpy(nuevaEntradaArch->nombre, nombre, 7);
		nuevaEntradaArch->nombre[8] = '~';
	}

	nuevaEntradaArch->attributes = ATR_ARCHIVO;
	nuevaEntradaArch->firstClusterHi = 0;
	nuevaEntradaArch->firstClusterLow = 0;
	nuevaEntradaArch->fileSize = 0;

	escribir_cluster(fs, dirClusterPadre, clusterPadre, Sock);

	//cargo el elemento
	nuevoArch = calloc(1, sizeof(t_elemento));
	strcpy(nuevoArch->nombre, nombre);
	nuevoArch->tipo = ENTRADA_ARCH;
	nuevoArch->clusterDirectorio = dirClusterPadre;
	nuevoArch->primerCluster = 0;
	nuevoArch->tamanio = 0;
	nuevoArch->numeroEntradaEnDir= indiceEntradaArch;
	nuevoArch->contenido = NULL;

	//lo agrego a la lista de elementos
	if (dirPadre != NULL) {
		lista_agregar(dirPadre->contenido, nuevoArch);
	} else {
		lista_agregar(fs->rootDirectoryContent, nuevoArch);
	}
	//free(nuevaEntradaArch);

	i = 0;
	while (pathCortado[i] != NULL) {
		free(pathCortado[i]);
		i++;
	}

	free(pathCortado);

}

int32_t remover_arch(t_fs *fs, char *path, int32_t Sock) {
	Tentrada *dirElemento = NULL;
	t_elemento *elemento = obtener_elem(fs, path, &dirElemento);
	t_entrada *entrada;
	char *clus;
	int32_t cantClus;

	if (elemento == NULL) {
		return 0;
	}

	// Busco la longname y la borro
	clus = obtener_cluster_cacheG(fs, elemento->clusterDirectorio, Sock);
	entrada = (t_entrada*) (clus+ (elemento->numeroEntradaEnDir - 1) * TAM_ENTRADA_DIR);
	entrada->nombre[0] = ENT_DIR_LIBRE;

	cantClus = cantClusters(fs, elemento);
	if (cantClus > 0) {
			QuitarClusters(fs, elemento, cantClus, clus, Sock);
		}

	// busco la entrada y la borro
	entrada = (t_entrada*) (clus + elemento->numeroEntradaEnDir * TAM_ENTRADA_DIR);
	entrada->nombre[0] = ENT_DIR_LIBRE;

	lista_borrar(elemento, dirElemento );

	return 1;
}

bool escribir_arch(t_fs *fs, t_elemento *arch, char* buff, off_t offset,size_t tamACopiarTotal, int32_t Sock) {

	uint32_t offsetClusters = offset / fs->tamCluster; //puede no funcionar con valores grandes
	uint32_t numCluster = 0;
	char *cluster, *buffOffset, *clusterOffset;
	int32_t i, offsetEnClus;
	size_t tamCopiado, tamACopiarAux;

	if (offset + tamACopiarTotal > arch->tamanio) {
		resize_arch(fs, arch, offset + tamACopiarTotal, Sock);
	}
	numCluster = arch->primerCluster;
	//obtengo el numCluster donde comienza el offset
	for (i = 0; i < offsetClusters; i++) {
		numCluster = ObtenerSgteCluster(fs, numCluster);
	}

	cluster = leer_cluster_cache_archivo(fs, arch, numCluster, Sock);

	//cluster = obtener_cluster_cacheG(fs, numCluster, Sock);
	clusterOffset = cluster + (offset - offsetClusters * fs->tamCluster);

	tamACopiarAux = fs->tamCluster - (offset - offsetClusters * fs->tamCluster); //tamaño para completar el clus desde el offset
	tamCopiado = 0;

	if (tamACopiarAux > tamACopiarTotal) {
		tamACopiarAux = tamACopiarTotal;
	}

	buffOffset = buff; // + (offset - offsetClusters * fs->tamCluster);

	do {

		memcpy(clusterOffset, buffOffset, tamACopiarAux); //estoy en duda de la parte del buff + tamCopiado


		escribir_cluster_cache_archivo(fs, arch, numCluster, cluster, Sock);

		//escribir_cluster(fs, numCluster, cluster, Sock);

		tamCopiado = tamCopiado + tamACopiarAux;

		if (tamCopiado < tamACopiarTotal) {

			numCluster = ObtenerSgteCluster(fs, numCluster);
			clusterOffset = obtener_cluster_cacheG(fs, numCluster, Sock);
			offsetEnClus = 0;
			if (tamCopiado + fs->tamCluster <= tamACopiarTotal) {
				tamACopiarAux = fs->tamCluster;
			} else {
				tamACopiarAux = tamACopiarTotal - tamCopiado;
			}
		}

	} while (tamCopiado < tamACopiarTotal);

	return true;
}

Tentrada *listarDir(t_fs *fs, char *path) {
	t_elemento *elemento;
	Tentrada *dirElem = NULL;

	if (strcmp(path, "/") == 0) {
		return fs->rootDirectoryContent;
	}

	elemento = obtener_elem(fs, path, &dirElem);

	if (elemento->tipo == ENTRADA_DIR) {
		return elemento->contenido;
	}

	return NULL;
}


/*unsigned char lfn_checksum(const unsigned char *pFcbName)
{
        int i;
        unsigned char sum=0;

        for (i=11; i; i--)
                sum = ((sum & 1) << 7) + (sum >> 1) + *pFcbName++;
        return sum;
}

*/



uint32_t cantClustersLibres(t_fs *fs) {

	uint32_t i=1, cantEntradasLibres= 0;

	while (i < fs->cantEntradasEnFAT) {
		if (NUM_CLUSTER(fs->tablaFAT[i]) == 0)
			cantEntradasLibres ++;
		i++;
	}
	return cantEntradasLibres;
}

uint32_t ObtenerUnClusLibre(t_fs *fs,int32_t *bloque) {

	uint32_t i = 0;
	*bloque = 0;
	while (i < fs->cantEntradasEnFAT) {
		if (NUM_CLUSTER(fs->tablaFAT[i]) == 0)
			return i;
		i++;
		if ( (i != 0) && (i % 256) == 0 ) (*bloque) ++;
	}
	return 0;

}



void cache_dump1 (Tentrada* directorio){


	TNodoLista *nodo = directorio->Nodo;
	t_elemento *elemento;

	while( nodo != NULL){

		elemento = (t_elemento*)nodo->dato;

		if(elemento->tipo == ENTRADA_ARCH){
			if(elemento->cache != NULL){
				grabar_cache(elemento->cache);
			}
		}
		if(elemento->tipo == ENTRADA_DIR){
			cache_dump1(elemento->contenido);
		}

		nodo = nodo->Next;

	}
}

void grabar_cache(t_cache * cache){
	int32_t i =1, j;
	FILE* archLog;
	archLog = fopen(PATH_CACHE_DUMP, "a+");
	t_cache* cacheAux;

	imprimir_cabecera(archLog);
	cacheAux = cache;
	while (cacheAux != NULL){
		fprintf(archLog,"Contenido de Bloque de Cache %d: \n", i);
		for(j=0;j<TAM_CLUSTER;j++){
			if ((j % 100) == 0)
				fprintf(archLog,"\n");
			fputc(cacheAux->cluster[j], archLog);
		}
		fprintf(archLog,"\n");
		cacheAux = (t_cache*)cacheAux->next;
		i++;
	}
	fclose(archLog);
}

void imprimir_cabecera(FILE *archivoLog){
	time_t tiempo = time(0);
	struct tm *tlocal = localtime(&tiempo);
	char timestamp[128];
	strftime(timestamp, 128, "%Y.%m.%d %H:%M:%S", tlocal);

	fprintf(archivoLog,"---------------------------------------------\n");
	fprintf(archivoLog,"Timestamp: %s \n", timestamp);
	fprintf(archivoLog,"Tamanio de Bloque de Cache: 4096 bytes \n");
	fprintf(archivoLog,"Cantidad de Bloques de Cache: %d \n", tamCache/4);

}






















